<template>
	<view class="vi_select">
		<view class="vi_slot" @click="openClick">
			<slot/>
		</view>
		<vi-dialog ref="viSelect"
			:point="setting.point"
			:radius="setting.boxRadius"
			:title="title"
			:useTitle="setting.useTitle"
			:spaceInside="inSpaceInside"
			:spaceOutside="inSpaceOutside"
			:topbar="topbar"
			:toolbar="toolbar"
			:bottombar="bottombar"
			:width="setting.width"
			:maxWidth="setting.maxWidth"
			:maxHeight="setting.maxHeight"
			:bodyHeight="setting.bodyHeight"
			:shift="shift"
			@event="onEvent">
			<view class="vi_selectPop">
				<!-- 多选 -->
				<vi-checkbox-group ref="viCheckboxGroup" v-if="isCheckbox&&!isTree"
					v-model="inValue"
					:groupId="id"
					:max="max"
					:disabled="disabled"
					:readOnly="readOnly">
					<view class="vi_select_item" :class="{'vi_select_bor':iconReverse}" :style="{'height':setting.optionHeight+'em'}" v-for="(item, index) in inList" :key="index">
						<vi-checkbox
							:groupId="id"
							:value="item[keySetting.value]"
							:type="setting.type"
							:color="color"
							:iconReverse="iconReverse"
							:icon="icon"
							:inline="false"
							:align="iconReverse?'between':'left'"
							:disabled="item.disabled"
							:readOnly="item.readOnly"
							:disabledMsg="item.disabledMsg"
							:readOnlyMsg="item.readOnlyMsg">
							<text class="vi_select_text">{{item[keySetting.name]}}</text>
						</vi-checkbox>
					</view>
				</vi-checkbox-group>
				<!-- 单选 -->
				<view class="vi_select_list" v-if="isRadio&&!isTree">
					<view v-for="(item, index) in inList" :key="index" @click="onItemClick(item)">
						<view class="vi_select_item" :class="{'vi_select_center':!iconReverse&&setting.point!='center'}" :style="itemStyle">
							<view class="vi_select_iconbox" :style="{'order':iconReverse?2:1}">
								<vi-style-controller
									control="text"
									:type="setting.type"
									:color="color"
									textHide
									:active="inValue==item[keySetting.value]"
									:disabled="item.disabled||disabled"
									:readOnly="item.readOnly||readOnly">
									<view class="vi_select_icon iconfont" :class="iconSetting.checked"></view>
								</vi-style-controller>
							</view>
							<view class="vi_select_text" :style="{'order':iconReverse?1:2,}">
								<vi-style-controller
									control="text"
									:type="setting.type"
									:color="color"
									:active="inValue==item[keySetting.value]"
									:disabled="item.disabled||disabled"
									:readOnly="item.readOnly||readOnly">
									{{item[keySetting.name]}}
								</vi-style-controller>
							</view>
						</view>
					</view>
				</view>
				<!-- 树 -->
				<vi-tree ref="viTree" v-if="isTree" @change="onTreeChange" @ready="onTreeReady"
					:dataList="dataList"
					:value="inValue"
					:treeStyle="isTree==true?'tree':isTree"
					:mode="mode"
					:searchSet="treeSetting.searchSet"
					:keyword="treeSetting.keyword"
					:searchLeaf="treeSetting.searchLeaf"
					:type="setting.type"
					:color="color"
					:radius="treeSetting.radius"
					:checkboxPlain="treeSetting.checkboxPlain"
					:useIconStatus="treeSetting.useIconStatus"
					:iconNode="treeSetting.iconNode"
					:selectNode="treeSetting.selectNode"
					:autoNodeType="treeSetting.autoNodeType"
					:icon="iconSetting"
					:keys="keySetting"
					:text="textSetting"
					resFormat="value"
					:useToolbar="false"
					:disabled="disabled"
					:readOnly="readOnly"></vi-tree>
			</view>
		</vi-dialog>
	</view>
</template>

<script>
	export default {
		name:"viSelect",
		model:{
			prop:"value",
			event:"input"
		},
		props: {
			//主题色，非按钮
			type:{
				type:[String,null],
				default:null
			},
			color:{
				type:[String,null],
				default:""
			},
			//外框用
			boxRadius:{
				type:[String,Number,null],
				default:null
			},
			buttonHeight:{
				type:[Number,null],
				default:0
			},
			iconReverse:{
				type:[Boolean,null],
				default:false
			},
			title:{
				type:[String,null],
				default:""
			},
			useTitle:{
				type:[Boolean,null],
				default:null
			},
			dataList:{
				type:[Array,Object,null],
				default:()=>[
					// {
					// 	name:"",
					// 	value:""
					// }
				]
			},
			value:{
				type:[String,Number,Array,Boolean,null],
				default:""
			},
			point:{
				type:[String,null],
				default:""
			},
			//组件类型，radio/show单选列表、checkbox多选列表
			mode:{
				type:[String,null],
				default:""
			},
			//指定是树，可选值tree树、page翻页树
			isTree:{
				type:[Boolean,String,null],
				default:false
			},
			//树选择模式，isTree时有效
			treeSet:{
				type:[Object,null],
				default:()=>({})
			},
			//返回数据格式，树模式，非树模式有差异
			//可选，value、string(多选时使用拼接串格式返回)、option(返回相同数据结构的选项对象，携带name、value属性)。为空等同value
			resFormat:{
				type:[String,null],
				default:""
			},
			//单选立即提交
			activeSubmit:{
				type:[Boolean,null],
				default:false
			},
			//使用全选、清空按钮，checkbox模式有效
			useSelectAll:{
				type:[Boolean,null],
				default:null
			},
			//使用查看选中数量，checkbox、tree、page模式有效
			useSelectNum:{
				type:[Boolean,null],
				default:null
			},
			//树模式使用清空按钮
			useTreeClear:{
				type:[Boolean,null],
				default:null
			},
			//使用重置按钮，tree、page模式有效
			useTreeReset:{
				type:[Boolean,null],
				default:null
			},
			//最少选择数，对单选也有效
			min:{
				type:[Number,null],
				default:0
			},
			//最大选择数
			max:{
				type:[Number,null],
				default:0
			},
			width:{
				type:[String,null],
				default:""
			},
			maxWidth:{
				type:[String,null],
				default:""
			},
			maxHeight:{
				type:[String,null],
				default:""
			},
			//内容区高度
			bodyHeight:{
				type:[String,null],
				default:""
			},
			//选项高度，非树使用，同时用于计算最大高度
			optionHeight:{
				type:[Number,null],
				default:0
			},
			//窗口偏移
			shift:{
				type:[Object,null],
				default:()=>({})
			},
			//框外间距
			spaceOutside:{
				type:[Number,String,Array,null],
				default:null
			},
			//框内间距
			spaceInside:{
				type:[Number,String,Array,null],
				default:null
			},
			//键名设置
			keys:{
				type:[Object,null],
				default:()=>({})
			},
			disabled:{
				type:[Boolean,null],
				default:false
			},
			readOnly:{
				type:[Boolean,null],
				default:false
			},
			//图标设置
			icon:{
				type:[Object,null],
				default:()=>({})
			},
			text:{
				type:[Object,null],
				default:()=>({})
			},
		},
		data() {
			return {
				id:null,
				inValue:null,
				inList:[],
				selectItem:null,//单选用
				dicTreeSet:{
					searchSet:{open:true},
					keyword:"",
					searchLeaf:true,
					type:this.type||"primary",
					color:this.color,
					radius:"circle",
					checkboxPlain:false,
					useIconStatus:true,
					useIconNode:true,
					selectNode:false,
					autoNodeType:true,
				},
				dicKeys:{
					name:"name",
					value:"value",//普通下拉菜单与树公用，可能冲突
					children:"children"
				},
				dicIcon:{
					checked:"icon-checked"
				},
				dicText:{
					confirm: "确定",
					cancel: "取消",
					showSelect: "查看已选",
					clear: "清空",
					reset: "重置",
					selected: "已选",
					selectAll: "全选",
					tipSelectMinError: "请至少选择${min}项",
					tipSelectMaxError: "最多可选${max}项",
				},
			}
		},
		computed: {
			//将默认值内含到css、计算属性等地方，只内含真值
			//规避特殊使用的情况下，由prop传入undefined、null等无效数据导致的，默认值失效问题。例如组件嵌套且需要组件间参数传递
			//HBuilder X3.3.13; 微信开发者工具stable 1.05.2111300
			//微信小程序会对传入的undefined、null值根据参数类型输出不同空值(false、""、null、0)，参数类型为null或未指定参数类型则输出null
			//H5会对传入的undefined取默认值，未设默认值则输出undefined。对传入的null值固定输出null
			//做参数传递时需警惕两者行为不同而造成的陷阱，主要问题出现在微信小程序的空值转化，输出非用户传入的空值、假值，使默认真值丢失
			//兼容空值取默认值的可行方案目前为，参数类型加入null如[Boolean,null]，判断传入参数是否为定义的类型，是则使用，否则赋予默认值
			setting(){
				return {
					useTitle: this.$api.isBoolean(this.useTitle)? this.useTitle :true,
					point: this.point ||"bottom",
					type: this.$api.isDefined(this.type)? this.type :"primary",
					boxRadius: this.$api.isDefined(this.boxRadius)? this.boxRadius :"0.5em",
					buttonHeight: this.buttonHeight ||3,
					optionHeight: this.optionHeight ||3,
					min: this.min ||0,
					useSelectAll: this.$api.isBoolean(this.useSelectAll)? this.useSelectAll :true,
					useSelectNum: this.$api.isBoolean(this.useSelectNum)? this.useSelectNum :true,
					useTreeClear: this.$api.isBoolean(this.useTreeClear)? this.useTreeClear :true,
					useTreeReset: this.$api.isBoolean(this.useTreeReset)? this.useTreeReset :true,
					width: this.width ||"75vw",
					maxWidth: this.maxWidth ||"40em",
					maxHeight: this.maxHeight ||"80vh",
					bodyHeight: this.bodyHeight? this.bodyHeight :(this.isTree?"65vh":""),
				}
			},
			//树设置
			treeSetting(){
				if(this.$api.isObject(this.treeSet)){
					return Object.assign({},this.dicTreeSet,this.treeSet)
				}
				return this.dicTreeSet
			},
			//键名设置
			keySetting(){
				if(this.$api.isObject(this.keys)){
					return Object.assign({},this.dicKeys,this.keys)
				}
				return this.dicKeys
			},
			iconSetting(){
				if(this.$api.isObject(this.icon)){
					return Object.assign({},this.dicIcon,this.icon)
				}
				return this.dicIcon
			},
			textSetting(){
				if(this.$api.isObject(this.text)){
					return Object.assign({},this.dicText,this.text)
				}
				return this.dicText
			},
			//顶部按钮
			topbar(){
				//底部弹窗，多选、树、或单选非立即提交模式
				if(this.setting.point == "bottom"&&(!this.isRadio||this.isTree||!this.activeSubmit)){
					return {
						buttonHeight:this.setting.buttonHeight,
						aside:true,
						list:[
							{event:"cancel", name: this.textSetting.cancel, close:true},
							{event:"confirm", name: this.textSetting.confirm, type:this.setting.type, color:this.color, disabled:this.disabledSubmit}
						]
					}
				}
				return {}
			},
			//工具按钮
			toolbar(){
				let res = {}
				let useSelectNum = this.setting.useSelectNum;
				if(this.isTree||this.isCheckbox){
					Object.assign(res,{
						type:this.setting.type,
						color:this.color,
						buttonHeight:this.setting.buttonHeight,
						list:[]
					});
				}
				if(this.isTree){
					if(useSelectNum){
						res.list.push({event:"showSelectList", name: this.textSetting.showSelect+"("+this.selectNum+")"})
					}
					if(this.setting.useTreeClear){
						res.list.push({event:"clearAll", name: this.textSetting.clear})
					}
					if(this.setting.useTreeReset){
						res.list.push({event:"reset", name: this.textSetting.reset})
					}
				}else if(this.isCheckbox){
					if(useSelectNum){
						res.list.push({event:"none", name: this.textSetting.selected+"("+this.selectNum+")", type:"default"})
					}
					if(this.setting.useSelectAll){
						res.list.push({event:"selectAll", name: this.textSetting.selectAll});
						res.list.push({event:"clear", name: this.textSetting.clear});
					}
				}
				return res
			},
			bottombar(){
				//单选非树，且立即提交。只给一个取消按钮
				if(this.isRadio&&!this.isTree&&this.activeSubmit){
					return {
						buttonHeight:this.setting.buttonHeight,
						line:true,
						lineHeight:"0.2em",
						backgroundColor:"#fdfdfd",
						list:[
							{event:"cancel", name: this.textSetting.cancel, close:true, bold:true},
						]
					}
				}else if(this.setting.point!="bottom"){
					//非底部弹窗，给确定、取消按钮
					return {
						buttonHeight:this.setting.buttonHeight,
						list:[
							{event:"cancel", name: this.textSetting.cancel, close:true},
							{event:"confirm", name: this.textSetting.confirm, type:this.setting.type, color:this.color}
						]
					}
				}
				//底部弹窗使用顶部按钮
				return {}
			},
			//单选
			isRadio(){
				return !this.mode||this.mode=="radio"||this.mode=="show"
			},
			isCheckbox(){
				return this.mode=="checkbox"
			},
			inSpaceInside(){
				let res = this.spaceInside;
				if(!this.$api.isValue(res)){
					switch(this.setting.point){
						case "bottom":
							if(this.isCheckbox){
								return [0,0,"0.5em",0]
							}
							return 0
						case "center":
							return 0
						default:
							return ["0.5em",0,0,0]
					}
				}
				return res
			},
			inSpaceOutside(){
				let res = this.spaceOutside;
				if(!this.$api.isValue(res)){
					switch(this.setting.point){
						case "top":
							return "0.5em"
						case "bottom":
							return ["0.5em","0.5em","1.5em","0.5em"]
						default:
							return 0
					}
				}
				return res
			},
			itemStyle(){
				let res ={
					"height": this.setting.optionHeight +"em"
				};
				if(this.iconReverse){
					res["justify-content"] = "space-between";
				}else if(this.setting.point=="center"){
					res["justify-content"] = "flex-start"
				}else{
					res["justify-content"] = "center";
					res["border-bottom"] = 0;
				}
				return this.$api.objToStr(res)
			},
			selectNum(){
				if(this.$api.isArray(this.inValue)){
					return this.inValue.length
				}else{
					return this.$api.isValue(this.inValue)? 1:0
				}
			},
			//提交按钮状态
			disabledSubmit(){
				if(this.isRadio){
					return this.selectNum <(this.setting.min?1:0)
				}
				return this.selectNum <this.setting.min
			},
		},
		methods: {
			//单选非树点击
			onItemClick(item){
				if(item.disabled||this.disabled){
					if(item.disabledMsg){
						this.$api.msg(item.disabledMsg, "info")
					}
					return
				}
				if(item.readOnly||this.readOnly){
					if(item.readOnlyMsg){
						this.$api.msg(item.readOnlyMsg, "info")
					}
					return
				}
				//注意0==""
				if(this.inValue!==item[this.keySetting.value]){
					this.inValue = item[this.keySetting.value];
					this.selectItem = item;
				}else if(!this.setting.min){//两次点击同样选项，只在min为0的情况(允许不选)取消选中
					this.inValue ="";
					this.selectItem =null;
				}
				if(this.activeSubmit&&!this.disabledSubmit){
					let res = this.resFormat=="option"?this.selectItem :this.inValue;
					this.$emit("input", res);
					this.$emit("submit", res);
					this.close()
				}
			},
			//按钮点击事件
			onEvent(e){
				switch(e.event){
					case "confirm":
						//单选
						let res = this.getData();
						this.$emit("input", res);
						this.$emit("submit", res);
						this.close();
						break;
					case "selectAll":
						this.$refs.viCheckboxGroup.selectAll()
						break;
					case "clear":
						this.$refs.viCheckboxGroup.clearAll()
						break;
					case "showSelectList":
						this.$refs.viTree.showSelectList()
						break;
					case "reset":
						this.inValue = this.value;
						this.$refs.viTree.initData();
						break;
					case "clearAll":
						this.$refs.viTree.clearAll()
						break;
				}
			},
			//树选中改变
			onTreeChange(e){
				this.inValue = e
			},
			onTreeReady(e){
				this.inValue = e
			},
			openClick(){
				if(!this.disabled&&!this.readOnly){
					this.open()
				}
			},
			open(){
				//重置选中值
				let value = this.$api.isValue(this.value)? this.$api.clone(this.value) :"";
				if(this.isCheckbox&&typeof(value)=="string"){//兼容字符串拼接串输入
					this.inValue = value? value.split(",") :[]
				}else{
					this.inValue = value;
				}
				this.$refs.viSelect.open();
			},
			close(){
				setTimeout(()=>{
					this.$refs.viSelect.close()
				},200)
			},
			//获取数据，外部使用
			getData(options){
				//单选
				let res;
				let resFormat = (options||{}).resFormat ||this.resFormat;
				if(this.isRadio){
					//返回选项
					if(resFormat=="option"){
						if(this.isTree){
							let node = this.$refs.viTree.getData({format: "node"})[0] ||null;
							if(node){
								res ={
									name: node[this.keySetting.name],
									value: node[this.keySetting.value],
								}
							}
						}else{
							res = this.selectItem
						}
					}else{//返回值
						res =this.inValue
					}
				}else{
					//多选
					let min = this.setting.min;
					let max = this.max? this.max :Infinity;
					let len = this.inValue? this.inValue.length :0;
					let msg;
					if(len<min){
						msg = this.textSetting.tipSelectMinError.replace("${min}", min);
						this.$api.msg(msg, "error");
						return
					}else if(len>max){
						msg = this.textSetting.tipSelectMaxError.replace("${max}", max);
						this.$api.msg(msg, "error");
						return
					}
					//返回选项
					if(resFormat=="option"){
						res =[];
						if(this.isTree){
							let nodes = this.$refs.viTree.getData({format: "node"});
							for(let node of nodes){
								res.push({
									name: node[this.keySetting.name],
									value: node[this.keySetting.value],
								})
							}
						}else{//非树从数据集中比对
							for(let val of this.inValue){
								let item = this.$api.arrFind(this.inList, {key:"value", value:val, format:"item"});
								if(item){
									res.push(item)
								}
							}
						}
					}else if(resFormat=="string"){
						res = this.inValue.join();
					}else{//返回值列表
						res = this.inValue;
					}
				}
				return res
			}
		},
		watch:{
			"value":{
				immediate:true,
				handler(newValue){
					if(this.isCheckbox&&typeof(newValue)=="string"){//兼容字符串拼接串输入
						this.inValue = newValue? newValue.split(",") :[]
					}else{
						this.inValue = this.$api.clone(newValue);
					}
					//树由ready计算初始，非树在这里计算
					if(this.$api.isValue(newValue)&&!this.isTree&&this.isRadio){
						this.selectItem = this.$api.arrFind(this.inList, {key:"value", value:newValue, format:"item"});
					}
				}
			},
			"dataList":{
				immediate:true,
				handler(newValue){
					if(!this.isTree){
						this.inList = newValue||[];
					}
					//只计算选中。树由ready计算初始，非树在这里计算
					if(this.$api.isValue(this.value)&&newValue&&!this.isTree&&this.isRadio){
						this.selectItem = this.$api.arrFind(newValue, {key:"value", value:this.value, format:"item"});
					}
				}
			},
		},
		created() {
			this.id = `select${Math.ceil(Math.random() * 10e10).toString(36)}`
		}
	}
</script>

<style lang="scss">
.vi_select{
	width: 100%;
}
.vi_selectPop{
	height: 100%;
	.vi_select_list{
		.vi_select_icon{
			font-size: 130%;
		}
		.vi_select_item:last-child{
			border-bottom:0
		}
		.vi_select_center .vi_select_iconbox,
		.vi_select_center .vi_select_text{
			transform: translateX(-0.65em);
		}
	}
	.vi_select_item{
		display: flex;
		align-items: center;
		justify-content: space-between;
		width: 100%;
		line-height: 1;
		padding: 0 0.5em;
		box-sizing: border-box;
		&.vi_select_bor{
			border-bottom: 1px dashed $vi-borColor-light;
		}
		.vi_select_text{
			margin: 0 0.5em;
		}
	}
}
</style>
